package xyz.lwm.lazycat.utility;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.StringJoiner;
import java.util.StringTokenizer;
import java.util.function.Function;

/**
 * popular string util
 *
 * @author lwm
 */
public abstract class StringUtil {

    /**
     * 判断字符串是否为空白字符串, 空字符串也算空
     *
     * @param cs 字符串
     * @return true: 空
     */
    public static boolean isBlank(final CharSequence cs) {
        int strLen;
        if (cs == null || (strLen = cs.length()) == 0) {
            return true;
        }
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(cs.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    /**
     * 判断字符串是否不为空白字符串, 空字符串算空
     *
     * @param cs 字符串
     * @return true: 不为空
     */
    public static boolean isNotBlank(final CharSequence cs) {
        return !isBlank(cs);
    }

    /**
     * 判断字符串是否为空, 空字符串不算空
     *
     * @param cs 源字符串
     * @return true: 空
     */
    public static boolean isEmpty(final CharSequence cs) {
        return cs == null || cs.length() == 0;
    }

    public static boolean isNotEmpty(final CharSequence cs) {
        return !isEmpty(cs);
    }


    /**
     * 组合成 string
     *
     * @param list      t list
     * @param delimiter 分隔符
     * @param prefix    前缀
     * @param suffix    后缀
     * @param mapping   t 转 string
     * @param <T>       generic type
     * @return 结果
     */
    public static <T> String join(List<T> list,
                                  CharSequence delimiter,
                                  CharSequence prefix,
                                  CharSequence suffix,
                                  Function<T, String> mapping) {
        Assert.notNull(list, "list");
        Assert.notNull(mapping, "mapping");
        StringJoiner joiner = new StringJoiner(delimiter, prefix, suffix);
        for (T t : list) {
            joiner.add(mapping.apply(t));
        }
        return joiner.toString();
    }

    /**
     * 分解字符串为list
     *
     * @param joining   合并的字符串
     * @param delimiter 分隔符
     * @param prefix    前缀
     * @param suffix    后缀
     * @param mapping   string 转换为 <T>
     * @param <T>       generic type
     * @return list
     */
    public static <T> List<T> split(String joining,
                                    CharSequence delimiter,
                                    CharSequence prefix,
                                    CharSequence suffix,
                                    Function<String, T> mapping) {
        if (joining == null) {
            return Collections.emptyList();
        }
        Assert.notNull(mapping, "mapping");

        int begin = 0;
        int end = joining.length();
        if (isNotBlank(prefix)) {
            begin = prefix.length();
        }
        if (isNotBlank(suffix)) {
            end = end - suffix.length();
        }
        Assert.isTrue(begin <= end, "begin must less than end");

        StringTokenizer st = new StringTokenizer(joining.substring(begin, end), delimiter.toString());
        List<T> tokens = new ArrayList<>();
        while (st.hasMoreTokens()) {
            tokens.add(mapping.apply(st.nextToken()));
        }

        return tokens;
    }

    /**
     * 去空格
     */
    public static String trim(String str) {
        return (str == null ? "" : str.trim());
    }

    /**
     * 是否包含字符串
     *
     * @param str  验证字符串
     * @param strs 字符串组
     * @return 包含返回true
     */
    public static boolean inStringIgnoreCase(String str, String... strs) {
        if (str != null && strs != null) {
            for (String s : strs) {
                if (str.equalsIgnoreCase(trim(s))) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 是否正整型数字
     *
     * @param source 正整数型字符串
     * @return true: 是数字
     */
    public static boolean isIntNumber(String source) {
        if (isBlank(source)) {
            return false;
        }
        char[] chars = source.toCharArray();
        for (char c : chars) {
            if (!Character.isDigit(c)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 是否下划线字符串
     *
     * @param str chars
     * @return true: 是下划线
     */
    public static boolean isUnderline(String str) {
        return isNotEmpty(str) && str.contains("_");
    }

    /**
     * 驼峰 -> 下划线 + 小写
     *
     * @param camel 驼峰
     * @return underline
     */
    public static String camelToUnderline(String camel) {
        return fromCamel(camel, '_');
    }

    /**
     * 下划线字符串 -> 驼峰
     *
     * @param underline 下划线
     * @return 驼峰
     */
    public static String underlineToCamel(String underline) {
        return toCamel(underline, '_');
    }

    /**
     * 驼峰转小写并连接分割符
     *
     * @param camel    驼峰字符串
     * @param splitter 分割字符
     */
    public static String fromCamel(String camel, char splitter) {
        if (isEmpty(camel)) {
            return camel;
        }
        char[] source = camel.toCharArray();
        char[] dest = new char[source.length * 2];
        int size = 0;
        for (int i = 0; i < source.length; i++) {
            char c = source[i];
            if (Character.isUpperCase(c)) {
                if (i > 0) {
                    dest[size++] = splitter;
                }
                dest[size++] = Character.toLowerCase(c);
            } else {
                dest[size++] = c;
            }
        }
        return new String(Arrays.copyOf(dest, size));
    }

    /**
     * 转成驼峰
     *
     * @param str      字符串
     * @param splitter 分隔字符
     */
    public static String toCamel(String str, char splitter) {
        if (isEmpty(str)) {
            return str;
        }

        char[] source = str.toCharArray();
        char[] dest = new char[source.length];
        int size = 0;
        int splitterIndex = 0;
        for (int i = 0; i < source.length; i++) {
            char c = source[i];
            if (c == splitter) {
                splitterIndex = i;
            } else if (splitterIndex > 0) {
                dest[size++] = Character.toUpperCase(c);
                splitterIndex = 0;
            } else {
                dest[size++] = c;
            }
        }

        return new String(Arrays.copyOf(dest, size));
    }

    /**
     * 某字符转大写
     *
     * @param str     字符串
     * @param indexes 字符下标
     */
    public static String uppercaseChar(String str, int... indexes) {
        if (isEmpty(str) && indexes == null) {
            return str;
        }
        char[] chars = str.toCharArray();
        for (int index : indexes) {
            if (index < chars.length) {
                chars[index] = Character.toUpperCase(chars[index]);
            }
        }
        return new String(chars);
    }

    /**
     * 某字符转小写
     *
     * @param str     字符串
     * @param indexes 字符下标
     */
    public static String lowercaseChar(String str, int... indexes) {
        if (isEmpty(str) && indexes == null) {
            return str;
        }
        char[] chars = str.toCharArray();
        for (int index : indexes) {
            if (index < chars.length) {
                chars[index] = Character.toLowerCase(chars[index]);
            }
        }
        return new String(chars);
    }

    /**
     * <h2>字符串格式化</h2>
     * <p>例如:</p>
     * <pre>
     * 通常使用：format("this is {} for {}", "a", "b") =》 this is a for b;
     * 转义{}： format("this is \\{} for {}", "a", "b") =》 this is {} for a;
     * 转义\： format("this is \\\\{} for {}", "a", "b") =》 this is \a for b.
     * </pre>
     *
     * @param pattern 格式化字符串
     * @param args    替换参数
     * @return 格式化后的结果
     */
    public static String format(String pattern, Object... args) {
        return format(pattern, "{}", args);
    }

    /**
     * <h2>字符串格式化</h2>
     * <p>例如:</p>
     * <pre>
     * 假设 replacement 为 #{};
     * 通常使用：format("this is #{} for #{}", "a", "b") =》 this is a for b;
     * 转义{}： format("this is \\#{} for #{}", "a", "b") =》 this is #{} for a;
     * 转义\： format("this is \\\\#{} for #{}", "a", "b") =》 this is \a for b.
     * </pre>
     * <p>当然, replacement 也可以为 #{}, ?, []...</p>
     *
     * @param pattern     格式化字符串
     * @param replacement 替换内容
     * @param args        替换参数
     * @return 格式化后的结果
     */
    public static String format(String pattern, String replacement, Object[] args) {
        if (isEmpty(pattern) || isEmpty(replacement) || CollectionUtil.isEmpty(args)) {
            return pattern;
        }
        // :: 将 pattern 按 replacement 分割开, 然后依次替换 args, 并处理转义符 ::
        StringBuilder str = new StringBuilder(pattern.length() + 100);
        int start = 0, len = replacement.length();
        for (int i = 0; i < args.length; ) {
            int pos = pattern.indexOf(replacement, start);
            if (pos == -1) {
                break;
            }
            // 左侧连续占位符数量
            int escapeNum = getContinuousEscapeNumLeftSide(pattern, pos);
            // 有转义符时, 跳过最近转义符; 无则直接到 pos 位置
            str.append(pattern, start, escapeNum > 0 ? pos - 1 : pos);
            // 只有一个转义符时, replacement 是被转义的, 需保留; 同时因为 arg 未被替换, 下次循环接着使用该 arg
            Object arg = args[i];
            str.append(escapeNum == 1 ? replacement : arg);
            i += escapeNum == 1 ? 0 : 1;
            start = pos + len;
        }
        return str.append(pattern, start, pattern.length()).toString();
    }

    // 获取左侧连续占位符 '\' 数量
    private static int getContinuousEscapeNumLeftSide(String chars, int pos) {
        int num = 0;
        for (int i = pos - 1; i > -1; i--) {
            if (chars.charAt(i) == '\\') {
                num++;
            } else {
                break;
            }
        }
        return num;
    }

}
